package com.atguigu.daijia.common.aspect;

import com.atguigu.daijia.common.annotations.LoginValidate;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.common.util.AuthContextHolder;
import com.mysql.cj.util.StringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

@Component
@Aspect
@Slf4j
public class LoginValidateAspect {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 环绕通知方法，用于校验登录状态
     * 该方法在目标方法执行前后都会执行，主要用于登录校验
     *
     * @param proceedingJoinPoint 切面对象，用于获取执行目标方法的参数和返回值
     * @param loginValidate 登录校验注解对象，包含登录校验的相关信息
     * @return 目标方法的返回值
     * @throws Throwable 如果目标方法执行过程中出现异常，该异常会被抛出
     */
    @Around("execution(* com.atguigu.daijia.*.controller.*.*(..)) && @annotation(loginValidate)")
    public Object loginValidate(ProceedingJoinPoint proceedingJoinPoint , LoginValidate loginValidate) throws Throwable {

        // 获取当前请求的请求属性
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 将请求属性转换为Servlet请求属性
        ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
        // 从请求头中获取token
        String token = Objects.requireNonNull(sra).getRequest().getHeader("token");
        // 如果token为空，抛出登录校验异常
        if(StringUtils.isNullOrEmpty(token)){
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        // 从Redis中根据token获取用户ID
        String id = (String) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
        // 如果用户ID不为空，设置用户ID到上下文中，用于后续操作
        if(!StringUtils.isNullOrEmpty(id)){
            AuthContextHolder.setUserId(Long.parseLong(id));
        }
        // 执行目标方法并返回其结果
        return proceedingJoinPoint.proceed();
    }
}
